package com.kent.hellokotlin.cClassAndObject

import kotlin.properties.Delegates
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

//委托属性
class Example1 {
    var p: String by Delegate()
}

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef get value of ${property.name}"
    }

    operator fun setValue(thisRef: Any, property: KProperty<*>, s: String) {
        println("$s has been assigned to ${property.name} in $thisRef")
    }
}

private fun foo() {
    val e = Example1()
    e.p = "this is p"
    println(e.p)
}

//延迟属性 Lazy
private fun foo1() {
    val lazyValue: String by lazy {
        println("lazyValue init")
        "Hello"
    }
    println(lazyValue)
    println(lazyValue)
}

//可观察属性 Observable
private fun foo2() {
    var your_name: String by Delegates.observable("cherry") { property, oldValue, newValue ->
        println("${property.name} was: $oldValue ,now is: $newValue")
    }
    your_name = "henry"
    your_name = "Kent"


    var yourName: String by Delegates.vetoable("default") { property, oldValue, newValue ->
        val result: Boolean = newValue.length == 4
        result
    }
    println(yourName)
    yourName = "henry"
    println(yourName)
    yourName = "Kent"
    println(yourName)
}

//把属性储存在映射中
/*
一个常见的用例是在一个映射（map）里存储属性的值。
这经常出现在像解析 JSON 或者做其他“动态”事情的应用中。
在这种情况下，你可以使用映射实例自身作为委托来实现委托属性。
*/
private fun foo3() {
    //使用by映射则 user或map 的name，age值发生改变时 map或者user 的name，age也会相应的改变
    class User(val map: MutableMap<String, Any?>) {
        var name: String by map
        var age: Int by map
    }

    val map: MutableMap<String, Any?> = mutableMapOf("name" to "Henry", "age" to 23)
    val user: User = User(map)
    println(user.name)
    println(user.age)
    user.name = "Kent"
    user.age = 25

    for ((name, age) in map) {
        println("name: $name, age: $age")
    }

}

//局部委托属性（自 1.1 起）
private fun foo4() {
    fun example(computeFoo: () -> Int) {
        val memoized by lazy(computeFoo)

        if (memoized % 2 == 0) {
            //doSomething
        }
    }
}

//属性委托要求
/*这里我们总结了委托对象的要求。*/
/*
对于一个只读属性（即 val 声明的），委托必须提供一个操作符函数 getValue()，该函数具有以下参数：
thisRef —— 必须与 属性所有者 类型（对于扩展属性——指被扩展的类型）相同或者是其超类型。
property —— 必须是类型 KProperty<*> 或其超类型。
getValue() 必须返回与属性相同的类型（或其子类型）。
*/
class Resource
class Owner {
    val valResource: Resource by ResourceDelegate()
}

class ResourceDelegate {
    operator fun getValue(thisRef: Owner, property: KProperty<*>): Resource {
        return Resource()
    }
}

/*
对于一个可变属性（即 var 声明的），委托必须额外提供一个操作符函数 setValue()， 该函数具有以下参数：
thisRef —— 必须与 属性所有者 类型（对于扩展属性——指被扩展的类型）相同或者是其超类型。
property —— 必须是类型 KProperty<*> 或其超类型。
value — 必须与属性类型相同（或者是其超类型）。
*/
class Owner1 {
    var valResource: Resource by ResourceDelegate1()
}

class ResourceDelegate1 {
    operator fun getValue(thisRes: Owner1, property: KProperty<*>): Resource {
        return Resource()
    }
//    operator fun setValue(thisRes: Owner1,property: KProperty<*>,value:Any?){
//
//    }
}

operator fun ResourceDelegate1.setValue(thisRes: Owner1, property: KProperty<*>, value: Any?) {

}

//翻译规则
/*
在每个委托属性的实现的背后，Kotlin 编译器都会生成辅助属性并委托给它。
例如，对于属性 prop，生成隐藏属性 prop$delegate，而访问器的代码只是简单地委托给这个附加属性：
*/
class Type

//class C5 {
//    var prop: Type by PropDelegate()
//}

class PropDelegate : ReadWriteProperty<C5, Type> {
    override fun getValue(thisRef: C5, property: KProperty<*>): Type {
        TODO("Not yet implemented")
    }

    override fun setValue(thisRef: C5, property: KProperty<*>, value: Type) {
        TODO("Not yet implemented")
    }
}

class C5 {
    private val prop_delegate = PropDelegate()
    var prop: Type
        get() = prop_delegate.getValue(this, this::prop)
        set(value) = prop_delegate.setValue(this, this::prop, value)
}

fun main() {
    foo3()
}